/*
-----------------------------------------------------------------------------
This source file is part of OGRE
    (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2006 Torus Knot Software Ltd
Also see acknowledgements in Readme.html

You may use this sample code for anything you like, it is not covered by the
LGPL like the rest of the engine.
-----------------------------------------------------------------------------
*/

/*
-----------------------------------------------------------------------------
Filename:    BspCollision.cpp
Description: Somewhere to play in the sand...
-----------------------------------------------------------------------------
*/

#include "OgreReferenceAppLayer.h"
#include "ExampleRefAppApplication.h"
#include "OgreStringConverter.h"
#include <OgreSingleton.h>

//#define __DEBUGON__


#ifdef __DEBUGON__
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <iostream>
#include <string>

void showWin32Console()
{
	static const WORD MAX_CONSOLE_LINES = 10;
	int hConHandle;
	long lStdHandle;
	CONSOLE_SCREEN_BUFFER_INFO coninfo;
	FILE *fp;
	// allocate a console for this app
	AllocConsole();
	// set the screen buffer to be big enough to let us scroll text
	GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
	coninfo.dwSize.Y = MAX_CONSOLE_LINES;
	SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),
	coninfo.dwSize);
	// redirect unbuffered STDOUT to the console
	lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
	hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
	fp = _fdopen( hConHandle, "w" );
	*stdout = *fp;
	setvbuf( stdout, NULL, _IONBF, 0 );
	// redirect unbuffered STDIN to the console
	lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);
	hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
	fp = _fdopen( hConHandle, "r" );
	*stdin = *fp;
	setvbuf( stdin, NULL, _IONBF, 0 );
	// redirect unbuffered STDERR to the console
	lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE);
	hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
	fp = _fdopen( hConHandle, "w" );
	*stderr = *fp;
	setvbuf( stderr, NULL, _IONBF, 0 );
	// make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog
	// point to console as well
	std::ios::sync_with_stdio();
}
#endif

// Hacky globals


SceneNode* targetNode;
RaySceneQuery* rsq = 0;
SoccerPitch* soccerPitch;



// Event handler to add ability to alter curvature
class BspCollisionListener : public ExampleRefAppFrameListener
{
protected:
    typedef std::map<String, ApplicationObject*> ObjectMap;
public:
    BspCollisionListener(RenderWindow* win, CollideCamera* cam)
        : ExampleRefAppFrameListener(win, cam)
    {
    }


    bool frameEnded(const FrameEvent& evt)
    {
        /*

        #ifdef __DEBUGON__
        std::cout << std::endl << "Wait::Execute" << std::endl << std::endl;
        #endif

        */


        // local just to stop toggles flipping too fast
        static Real timeUntilNextToggle = 0;
        static Real timeUntilNextCheck = 0;

        static Real timeUntilNextPitchUpdate = 0;

        bool ret = ExampleRefAppFrameListener::frameEnded(evt);


        // Deal with time delays that are too large
        // If we exceed this limit, we ignore
        static const Real MAX_TIME_INCREMENT = 0.5f;
        if (evt.timeSinceLastEvent > MAX_TIME_INCREMENT)
        {
            return true;
        }

        if (timeUntilNextPitchUpdate >= 0)
            timeUntilNextPitchUpdate -= evt.timeSinceLastFrame;


        if (timeUntilNextToggle >= 0)
            timeUntilNextToggle -= evt.timeSinceLastFrame;

        if (timeUntilNextCheck >= 0)
            timeUntilNextCheck -= evt.timeSinceLastFrame;


/*
        if (ball->testCollide(box)){
            ball->getSteeringBehavior()->PursuitOn(boxAnt);
        }

        if (ball->testCollide(boxAnt)){
            ball->getSteeringBehavior()->PursuitOn(box);
        }

        if (fieldPlayer->testCollide(ball)){

            ball->setOwner(fieldPlayer);
        }

        if( !fieldPlayer->testCollide(plane) ){
            fieldPlayer->setPosition( fieldPlayer->getPosition().x,fieldPlayer->getPosition().y, 7 );
        }

        if (ball->getOwner() != NULL ){
            ball->setPosition( ball->getOwner()->getPosition() + ( ball->getOwner()->getLinearVelocity() / Ogre::Vector3( ball->getOwner()->getLinearVelocity() ).normalise() ) * 17 );
        }

        if (ball->getOwner() != NULL && mKeyboard->isKeyDown(OIS::KC_Z) && timeUntilNextToggle <= 0)
        {
            timeUntilNextToggle = 0;
            //ball->setPosition(mCamera->getPosition() +
            //    mCamera->getDirection() * mCamera->getNearClipDistance() * 2);
            ball->setLinearVelocity( ( ball->getOwner()->getLinearVelocity() / Ogre::Vector3( ball->getOwner()->getLinearVelocity() ).normalise() ) * Ogre::Vector3(800, 800, 0) );
            ball->setOwner( NULL );
        }
*/
        // Call superclass








        if (timeUntilNextPitchUpdate <= 0)
        {
            timeUntilNextPitchUpdate = 0.5f;
            soccerPitch->update();

        }




        /*
        if (mKeyboard->isKeyDown(OIS::KC_SPACE) && timeUntilNextToggle <= 0)
        {
            timeUntilNextToggle = 2;
            //ball->setPosition(mCamera->getPosition() +
            //    mCamera->getDirection() * mCamera->getNearClipDistance() * 2);
            fieldPlayer->setLinearVelocity(mCamera->getDirection().x*200, mCamera->getDirection().y*200, 30);
            fieldPlayer->setAngularVelocity(Vector3::ZERO);
        }


        if (fieldPlayer->testCollide(plane) && mKeyboard->isKeyDown(OIS::KC_I) && timeUntilNextToggle <= 0)
        {
            timeUntilNextToggle = 0;
            fieldPlayer->setLinearVelocity(0,150,0);
        }

        if (fieldPlayer->testCollide(plane) && mKeyboard->isKeyDown(OIS::KC_J) && timeUntilNextToggle <= 0)
        {
            timeUntilNextToggle = 0;
            fieldPlayer->setLinearVelocity(-150,0,0);
        }

        if (fieldPlayer->testCollide(plane) && mKeyboard->isKeyDown(OIS::KC_L) && timeUntilNextToggle <= 0)
        {
            timeUntilNextToggle = 0;
            fieldPlayer->setLinearVelocity(150,0,0);
        }

        if (fieldPlayer->testCollide(plane) && mKeyboard->isKeyDown(OIS::KC_K) && timeUntilNextToggle <= 0)
        {
            timeUntilNextToggle = 0;
            fieldPlayer->setLinearVelocity(0,-150,0);
        }

        if (fieldPlayer->testCollide(plane) && mKeyboard->isKeyDown(OIS::KC_S) && timeUntilNextToggle <= 0)
        {
            fieldPlayer->update();
        }
         fieldPlayer->update();

            */
        // Move the targeter
        rsq->setRay(mCamera->getRealCamera()->getCameraToViewportRay(0.5, 0.5));
        RaySceneQueryResult& rsqResult = rsq->execute();
        RaySceneQueryResult::iterator ri = rsqResult.begin();
        if (ri != rsqResult.end())
        {
            RaySceneQueryResultEntry& res = *ri;
            targetNode->setPosition(rsq->getRay().getPoint(res.distance));

        }


        // Perform simulation step
        //World::getSingleton().simulationStep(evt.timeSinceLastFrame);


        return ret;


    }

};


class BspCollisionApplication : public ExampleRefAppApplication
{
public:
    BspCollisionApplication() {

    }

    ~BspCollisionApplication()
    {
		delete rsq;
    }

protected:

    void chooseSceneManager(void)
    {
        mSceneMgr = mRoot->createSceneManager(ST_GENERIC, "MySceneManager");
    }
    void createWorld(void)
    {
        // Create BSP-specific world
        mWorld = new World(mSceneMgr, World::WT_REFAPP_GENERIC);
    }
    void createScene(void)
    {



        mSceneMgr->setShadowTechnique(SHADOWTYPE_STENCIL_MODULATIVE);
        // Set ambient light
        mSceneMgr->setAmbientLight(ColourValue(0.2, 0.2, 0.2));
        // Create a point light
        Light* l = mSceneMgr->createLight("MainLight");
        l->setPosition(400,200,100);
        l->setAttenuation(8000,1,0,0);


        // Setup World
        mWorld->setGravity(Vector3(0, 0, -60));
        //mWorld->getSceneManager()->setWorldGeometry("ogretestmap.bsp");

        // modify camera for close work
        mCamera->setNearClipDistance(10);
        mCamera->setFarClipDistance(20000);

        // Also change position, and set Quake-type orientation
        // Get random player start point
        ViewPoint vp = mSceneMgr->getSuggestedViewpoint(true);
        mCamera->setPosition(400,200,100);
        mCamera->pitch(Degree(90)); // Quake uses X/Y horizon, Z up
        mCamera->rotate(vp.orientation);
        // Don't yaw along variable axis, causes leaning
        mCamera->setFixedYawAxis(true, Vector3::UNIT_Z);
        // Look at the boxes
        mCamera->setPosition(0,200,400);
		mCamera->lookAt(400,200,0);



        /*
        fieldPlayer = mWorld->createFieldPlayer("fieldPlayer", 7, Vector3(400,200,7) );

        fieldPlayer->setDynamicsEnabled(true);
        fieldPlayer->getEntity()->setMaterialName("Ogre/Eyes");
        fieldPlayer->setTag(true);

        */
		//OgreRefApp::Box* box = mWorld->createBox("shelf", 75, 125, 5, Vector3(-150, 40, 30));
        //box->getEntity()->setMaterialName("Examples/Rocky");
        /*
        static const Real BOX_SIZE = 15.0f;
        static const int num_rows = 4;

        for (int row = 0; row < num_rows; ++row)
        {

            Real row_size = BOX_SIZE * 1.25;
            String name = "box";
            name += StringConverter::toString(row);
            box = mWorld->createBall(name, 7 ,
                Vector3(-150,
                    (BOX_SIZE / 2) + (row * BOX_SIZE),
                    BOX_SIZE / 2
                   ));
            box->setDynamicsEnabled(false, true);
            box->getEntity()->setMaterialName("Examples/10PointBlock");
            box->setTag(true);

            //box->setLinearVelocity(0,400,0);
            //box->getSteeringBehavior()->InterposeOn(ball, box);
            boxAnt=box;
            //box->getSteeringBehavior()->SeekOn();
            //box->getSteeringBehavior()->SetTarget(ball->getPosition());

        }

        */
/*
        ball = mWorld->createBall("ball", 7 ,
                Vector3(250,
                    250,
                    10
                   ));
            ball->setDynamicsEnabled(true, true);
            ball->getEntity()->setMaterialName("Examples/10PointBlock");
            ball->setTag(true);
            */
        //ball->getSteeringBehavior()->InterposeOn(box,boxAnt);
        //ball->getSteeringBehavior()->InterposeOn(boxAnt,box);
        /*
        mWorld->createBox("wall-top", 800, 1, 800, Vector3(400, 0, 0));
        mWorld->createBox("wall-right", 1, 400, 800, Vector3(0, 200, 0));
        mWorld->createBox("wall-down", 800, 1, 800, Vector3(400, 400, 0));
        mWorld->createBox("wall-left", 1, 400, 800, Vector3(800, 200, 0));
        */

        mCamera->setCollisionEnabled(false);
        mCamera->getRealCamera()->setQueryFlags(0);
        /*
        plane = mWorld->createPlane("field",400,200);
        plane->setPosition(400,200,0);
        plane->getEntity()->setMaterialName("Examples/GrassFloor");
        */
        // Create the targeting sphere
        Entity* targetEnt = mSceneMgr->createEntity("testray", "sphere.mesh");
        MaterialPtr mat = MaterialManager::getSingleton().create("targeter",
            ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
        Pass* pass = mat->getTechnique(0)->getPass(0);
        TextureUnitState* tex = pass->createTextureUnitState();
        tex->setColourOperationEx(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT,
            ColourValue::Red);
        pass->setLightingEnabled(false);
        pass->setSceneBlending(SBT_ADD);
        pass->setDepthWriteEnabled(false);


        targetEnt->setMaterialName("targeter");
        targetEnt->setCastShadows(false);
        targetEnt->setQueryFlags(0);
        targetNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
        targetNode->scale(0.025, 0.025, 0.025);
        targetNode->attachObject(targetEnt);

        rsq = mSceneMgr->createRayQuery(Ray());
        rsq->setSortByDistance(true, 1);
        //rsq->setWorldFragmentType(SceneQuery::WFT_SINGLE_INTERSECTION);
        soccerPitch = mWorld->createSoccerPitch("soccerPitch",0,1);


    }
    // Create new frame listener
    void createFrameListener(void)
    {

        mFrameListener= new BspCollisionListener(mWindow, mCamera);
        mRoot->addFrameListener(mFrameListener);


    }

public:

};



#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"


INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
int main(int argc, char **argv)
#endif
{
    // Create application object
    BspCollisionApplication app;


    #ifdef __DEBUGON__
    showWin32Console();
    std::cout << "Iniciando App" << std::endl;
    #endif

    try {
        app.go();
    } catch( Exception& e ) {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
        MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
        std::cerr << "An exception has occured: " << e.getFullDescription();
#endif
    }




    return 0;
}







